import math, cairo, pyglet
from ctypes import *

WIDTH, HEIGHT = 512, 512

def draw_stuff(ctx):
    ctx.scale(WIDTH, HEIGHT)

    pat = cairo.LinearGradient(0.0, 0.0, 0.0, 1.0)
    pat.add_color_stop_rgba(1, 1.0, 0, 0, 1.0)
    pat.add_color_stop_rgba(0, 0.0, 1.0, 0.2, 1)
    
    ctx.rectangle(0,0,1,1)
    ctx.set_source(pat)
    ctx.fill()

    ctx.translate (0.1, 0.1) # Changing the current transformation matrix

    ctx.move_to (0, 0)
    ctx.arc (0.2, 0.1, 0.1, -math.pi/2, 0) # Arc(cx, cy, radius, start_angle, stop_angle)
    ctx.line_to (0.5, 0.1) # Line to (x,y)
    ctx.curve_to (0.5, 0.2, 0.5, 0.4, 0.2, 0.8) # Curve(x1, y1, x2, y2, x3, y3)
    ctx.close_path ()
    
    ctx.set_source_rgb (0.3, 0.2, 0.5) # Solid color
    ctx.set_line_width (0.02)
    ctx.stroke ()


import shp
import pyglet
from pyglet.gl import *

class MainWindow(pyglet.window.Window):
    def __init__(self):
        super(MainWindow, self).__init__(width=WIDTH, height=HEIGHT,resizable=False, visible=False, fullscreen=False)
        self.set_caption("Phillip's GIS")

        # Set up the key handler
        self.keys = pyglet.window.key.KeyStateHandler()
        self.push_handlers(self.keys)

        # Call the update method every 60th of a second.
        #pyglet.clock.schedule_interval(self.update, 1/60.0)
        
        # Setup OpenGL settings.
        glDisable(GL_DEPTH_TEST)
        glEnable(GL_CULL_FACE)
        glDisable(GL_DITHER)
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glClearColor(1,1,1,1)
        glPointSize(3.0)


        # Setup map stuff.
        self.map = GISMap()
        self.create_surface()
        self.draw_cairo()

    def draw_cairo(self):
        self.map.draw_cairo(self.ctx)
        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, self.data)

    def on_resize(self, width, height):        
        aspect_ratio = float(width) / float(height)
        self.view_width = WIDTH #self.map.width
        self.view_height = HEIGHT #self.map.height
        glViewport(0, 0, width, height)
        self.set_projection()

    def set_projection(self):
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        glOrtho(0, self.view_width, 0, self.view_height, -1, 1)
        #print self.view_width, self.view_height
        glMatrixMode(GL_MODELVIEW)

    def create_surface(self):
        self.data = (c_ubyte*WIDTH*HEIGHT*4)()
        stride = WIDTH*4
        surface = cairo.ImageSurface.create_for_data(self.data, cairo.FORMAT_ARGB32, WIDTH, HEIGHT, stride)
        self.ctx = cairo.Context(surface)
        #draw_stuff(self.ctx)

        self.texture = pyglet.image.Texture.create_for_size(GL_TEXTURE_2D, WIDTH, HEIGHT, GL_RGBA)
        #glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, self.data)
        #surface.write_to_png("example.png")

    def on_draw(self):
        glLoadIdentity()
        glEnable(GL_TEXTURE_2D)
        glBindTexture(GL_TEXTURE_2D, self.texture.id)
        glBegin(GL_QUADS)
        glTexCoord2f(0,0)
        glVertex2f(0,0)
        glTexCoord2f(1,0)
        glVertex2f(WIDTH,0)
        glTexCoord2f(1,1)
        glVertex2f(WIDTH,HEIGHT)
        glTexCoord2f(0,1)
        glVertex2f(0,HEIGHT)
        glEnd()
        glDisable(GL_TEXTURE_2D)
        
    def update(self, dt):
        pass

    def on_key_press(self, symbol, modifiers):
        if symbol == pyglet.window.key.ESCAPE:
            pyglet.app.exit()
        elif symbol == pyglet.window.key._0 or symbol == pyglet.window.key.F:
            self.window.set_fullscreen(not self.window.fullscreen)
        elif symbol == pyglet.window.key.A:
            self.translate_map(0,50)

    def on_mouse_press(self, x, y, button, modifiers):
        self.start_point = (x,y)
        
    def on_mouse_release(self, x, y, button, modifiers):
        if not self.start_point: return
        self.end_point = (x, y)
        dx = self.end_point[0] - self.start_point[0]
        dy = self.end_point[1] - self.start_point[1]
        self.translate_map(-dx, -dy)
        self.start_point = None
        
    def on_mouse_drag(self, x, y, dx, dy, button, modifiers):
        self.start_point = (x,y)
        self.translate_map(-dx, -dy)        

    def translate_map(self, dx, dy):
        maxdim = max(self.map.width, self.map.height)
        dx *= float(maxdim)/WIDTH
        dy *= float(maxdim)/HEIGHT
        self.map.x0 += dx
        self.map.y0 += dy
        self.draw_cairo()

        
class GISMap(object):
    def __init__(self):
        self.facilities = shp.Shapefile('sourcedata/facilities/facilities.shp')

        self.x0 = self.facilities.xmin
        self.y0 = self.facilities.ymin
        self.width = self.facilities.xmax - self.facilities.xmin
        self.height = self.facilities.ymax - self.facilities.ymin

        self.streets = shp.Shapefile('sourcedata/STREETS/STREETS.shp')
        self.parks = shp.Shapefile('sourcedata/coa_parks/city_of_austin_parks.shp')
        self.counties = shp.Shapefile('sourcedata/counties/capcog_counties.shp')

        self.x0 = self.streets.xmin
        self.y0 = self.streets.ymin
        self.width = self.streets.xmax - self.streets.xmin
        self.height = self.streets.ymax - self.streets.ymin        

        # zoom in
        cx,cy = (self.x0 + self.width/2.0, self.y0 + self.height/2.0)
        scale = 0.25
        self.width *= scale
        self.height *= scale
        self.x0 = cx - self.width/2.0
        self.y0 = cy - self.height/2.0


    def draw_points(self, ctx, points):
        # Draw points as zero length lines.
        for p in points:
            ctx.move_to(p.x,p.y)
            ctx.line_to(p.x,p.y)
            
        # Stroke the points under a different transform
        # so that the linewidth will be scaled properly.
        ctx.save()
        ctx.identity_matrix()
        ctx.set_source_rgb(1, 0, 0)
        ctx.set_line_width(5)
        ctx.set_line_cap(cairo.LINE_CAP_ROUND)
        ctx.stroke()
        ctx.restore()

    def draw_lines(self, ctx, lines):
        for line in lines:
            ctx.move_to(*line.vertices[0])
            for x,y in line.vertices[1:]:
                ctx.line_to(x,y) 

        # Stroke the lines under different transform
        # so that linewidths are scaled properly.
        ctx.save()
        ctx.identity_matrix()
        ctx.set_source_rgb(0, 0, 0)
        ctx.set_line_width(.5)
        ctx.set_line_cap(cairo.LINE_CAP_ROUND)
        ctx.stroke()
        ctx.restore()           

    def draw_polygons(self, ctx, polygons):
        for polygon in polygons:
            ctx.move_to(*polygon.vertices[0])
            for x,y in polygon.vertices[1:]:
                ctx.line_to(x,y)
            ctx.close_path()
        
        ctx.set_source_rgb(0.1, 0.8, 0.1)
        ctx.fill()

    def draw_polygon_outlines(self, ctx, polygons):
        for polygon in polygons:
            ctx.move_to(*polygon.vertices[0])
            for x,y in polygon.vertices[1:]:
                ctx.line_to(x,y)
            ctx.close_path()
        
        # Stroke the lines under different transform
        # so that linewidths are scaled properly.
        ctx.save()
        ctx.identity_matrix()
        ctx.set_source_rgb(.5, 0.1, 0.6)
        ctx.set_line_width(2)
        ctx.set_line_cap(cairo.LINE_CAP_ROUND)
        ctx.stroke()
        ctx.restore()           
                
    def draw_cairo(self, ctx):
        """Draws the map background and all the objects in the map."""        

        print 'draw_cairo'
        # Fill in the background with white.
        ctx.identity_matrix()
        ctx.rectangle(0,0,WIDTH,HEIGHT)
        ctx.set_source_rgb(1,1,1)
        ctx.fill()

        maxdim = max(self.width, self.height)
        # Apply a transform for data to show up in the context.
        ctx.scale(WIDTH/maxdim, HEIGHT/maxdim)
        ctx.translate(-self.x0, -self.y0)
        
        #self.draw_polygon_outlines(ctx, self.counties.polygons)
        #self.draw_lines(ctx, self.streets.polylines)
        #self.draw_polygons(ctx, self.parks.polygons)
        self.draw_points(ctx, self.facilities.points)

    def update(self, dt):
        pass



def run_application():
    window = MainWindow()
    window.clear()
    window.flip()
    window.set_visible(True)
    pyglet.app.run()

if __name__ == '__main__':
    run_application()
